﻿///////////////////////////////////////////////////////////////////////////////
//
//  This source is part of the  "Managed Task Framework.NET Runtime"  (MTF.NET Runtime).
//
//
//  Copyright (c) 2011, Multicore Consulting Limited (UK)
//  All rights reserved.
//  http://www.multicoreconsulting.co.uk
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions
//  are met:
//
//  -  Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//  -  Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in
//     the documentation and/or other materials provided with the
//     distribution.
//  -  Neither the name of Multicore Consulting Limited nor the names of
//     its contributors may be used to endorse or promote products
//     derived from this software without specific prior written
//     permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
//  COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
//  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
//  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//  POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MTFOrg.MTF.MTFRuntime;
using MTFOrg.MTF.MTFCore.Messages;

namespace MTFOrg.MTF.MTFCore
{
    /// <summary>
    /// Describes the possible states of a slot.
    /// </summary>
    public enum LinkStatus : byte
    {
        Inactive,
        Pending,
        Opened,
        Waiting,
        Released
    }

    /// <summary>
    /// Data store for producer and consumer slot status information.
    /// </summary>
    public class Slots
    {
        /// <summary>
        /// Number representing a non-existent or undefined slot.
        /// </summary>
        public const int NO_SLOT = -1;

        /// <summary>
        /// Number representing a non-existent or undefined link.
        /// </summary>
        public const int NO_LINK = -1;

        /// <summary>
        /// Slot set byte data store.
        /// </summary>
        /// <remarks>
        /// The slot set byte data store contains contiguous slot set data in the following structure:
        /// 
        /// ByteStore
        /// {
        ///     ConsumerLinkStatii : int (4-bits) [numConsumerSlots][numConsumerLinks];
        ///     ProducerLinkStatii : int (4-bits) [numProducerSlots][numProducerLinks];
        /// }
        /// </remarks>
        byte[] m_ByteData;

        /// <summary>
        /// Slot set int data store.
        /// </summary>
        /// <remarks>
        /// The slot set int data store contains contiguous slot set data in the following structure:
        /// 
        /// IntStore
        /// {
        ///     ConsumerOppositeLinks[numConsumerLinks];
        ///     ConsumerSlotsInUse[numConsumerLinks];
        ///     ConsumerSlots[numConsumerSlots]
        ///     {
        ///         ConsumerOppositeSlot;
        ///         ConsumerNumCompletedLinks;
        ///         ConsumerNextSlot;
        ///         ConsumerExternalSlot[numConsumerLinks];
        ///     }
        ///     ConsumerFreeSlots[numConsumerSlots];
        ///     ---
        ///     ProducerOppositeLinks[numProducerLinks];
        ///     ProducerSlotsInUse[numProducerLinks];
        ///     ProducerOppositeSlot[numProducerSlots];
        ///     ProducerFreeSlots[numProducerSlots];
        ///     ProducerNextSlots[numProducerSlots];
        /// }
        /// </remarks>
        int[] m_IntData;

        /// <summary>
        /// Number of consumer links.
        /// </summary>
        int m_NumConsumerLinks;

        /// <summary>
        /// Number of producer links.
        /// </summary>
        int m_NumProducerLinks;

        /// <summary>
        /// Number of consumer slots (overlay depth).
        /// </summary>
        int m_NumConsumerSlots;

        /// <summary>
        /// Number of producer slots
        /// </summary>
        int m_NumProducerSlots;

        /// <summary>
        /// Whether only one producer link is active at a time in a given slot.
        /// </summary>
        bool m_SingleActiveProducerLink;

        /// <summary>
        /// Number of producer links that can be simultanously active.
        /// </summary>
        int m_NumActiveProducerLinks;

        /// <summary>
        /// Array of tags for each consumer slot.
        /// </summary>
        object[] m_ConsumerTags;

        /// <summary>
        /// Array of tags for each producer slot.
        /// </summary>
        object[] m_ProducerTags;

        /// <summary>
        /// Offset between start of adjacent consumer slot sets.
        /// </summary>
        int m_ConsumerSlotSetStride;

        /// <summary>
        /// Offset of first producer opposite link value.
        /// </summary>
        int m_ProducerOppositeLinksStart;

        /// <summary>
        /// Read index for the free consumer slots array.
        /// </summary>
        int m_ConsumerFreeSlotsReadIdx;

        /// <summary>
        /// Write index for the free consumer slots array.
        /// </summary>
        int m_ConsumerFreeSlotsWriteIdx;

        /// <summary>
        /// Read index for the free producer slots array.
        /// </summary>
        int m_ProducerFreeSlotsReadIdx;

        /// <summary>
        /// Write index for the free producer slots array.
        /// </summary>
        int m_ProducerFreeSlotsWriteIdx;

        /// <summary>
        /// Number of producer slots that are in use.
        /// </summary>
        int m_NumProducerSlotsUsed;

        /// <summary>
        /// Number of consumer slots that are in use.
        /// </summary>
        int m_NumConsumerSlotsUsed;

        /// <summary>
        /// Sets the status for the specified consumer slot & link.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <param name="link">Link number</param>
        /// <param name="status">New status</param>
        public void SetConsumerLinkStatus(int slot, int link, LinkStatus status)
        {
            int nibble = slot * m_NumConsumerLinks + link;
            byte shift = (byte)(((~nibble) & 1) << 2);
            m_ByteData[nibble >> 1] &= (byte)(0xf0 >> shift);
            m_ByteData[nibble >> 1] |= (byte)((int)status << shift);
        }

        /// <summary>
        /// Gets the status for the specified consumer slot & link.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <param name="link">Link number</param>
        /// <returns>Status of requested slot & link</returns>
        public LinkStatus GetConsumerLinkStatus(int slot, int link)
        {
            int nibble = slot * m_NumConsumerLinks + link;
            byte shift = (byte)((~nibble & 1) << 2);
            return (LinkStatus)((m_ByteData[nibble >> 1] >> shift) & 0x0f);
        }

        /// <summary>
        /// Sets the status for the specified producer slot & link.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <param name="link">Link number</param>
        /// <param name="status">Status of requested slot & link</param>
        public void SetProducerLinkStatus(int slot, int link, LinkStatus status)
        {
            int nibble = m_NumConsumerSlots * m_NumConsumerLinks + slot * m_NumActiveProducerLinks;
            if (!m_SingleActiveProducerLink) nibble += link;
            byte shift = (byte)(((~nibble) & 1) << 2);
            m_ByteData[nibble >> 1] &= (byte)(0xf0 >> shift);
            m_ByteData[nibble >> 1] |= (byte)((int)status << shift);
        }

        /// <summary>
        /// Gets the status for the specfied producer slot & link.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <param name="link">Link index</param>
        /// <returns>Status of requested slot & link</returns>
        public LinkStatus GetProducerLinkStatus(int slot, int link)
        {
            int nibble = m_NumConsumerSlots * m_NumConsumerLinks + slot * m_NumActiveProducerLinks;
            if (!m_SingleActiveProducerLink) nibble += link;
            byte shift = (byte)((~nibble & 1) << 2);
            return (LinkStatus)((m_ByteData[nibble >> 1] >> shift) & 0x0f);
        }

        /// <summary>
        /// Sets the consumer opposite slot.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <param name="oppositeSlot">Opposite slot</param>
        public void SetConsumerOppositeSlot(int slot, int oppositeSlot)
        {
            m_IntData[2 * m_NumConsumerLinks + slot * m_ConsumerSlotSetStride] = oppositeSlot;
        }

        /// <summary>
        /// Gets the consumer opposite slot.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <returns>Opposite slot</returns>
        public int GetConsumerOppositeSlot(int slot)
        {
            return m_IntData[2 * m_NumConsumerLinks + slot * m_ConsumerSlotSetStride];
        }

        /// <summary>
        /// Sets the producer opposite slot.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <param name="oppositeSlot">Opposite slot</param>
        public void SetProducerOppositeSlot(int slot, int oppositeSlot)
        {
            m_IntData[m_ProducerOppositeLinksStart + 2 * m_NumProducerLinks + slot] = oppositeSlot;
        }

        /// <summary>
        /// Gets the producer opposite slot.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <returns>Opposite slot</returns>
        public int GetProducerOppositeSlot(int slot)
        {
            return m_IntData[m_ProducerOppositeLinksStart + 2 * m_NumProducerLinks + slot];
        }

        /// <summary>
        /// Sets the consumer next slot.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <param name="nextSlot">Next slot</param>
        public void SetConsumerNextSlot(int slot, int nextSlot)
        {
            m_IntData[2 * m_NumConsumerLinks + slot * m_ConsumerSlotSetStride + 2] = nextSlot;
        }

        /// <summary>
        /// Gets the consumer next slot.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <returns>Next slot</returns>
        public int GetConsumerNextSlot(int slot)
        {
            return m_IntData[2 * m_NumConsumerLinks + slot * m_ConsumerSlotSetStride + 2];
        }

        /// <summary>
        /// Sets the consumer external slot.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <param name="link">Link number</param>
        /// <param name="externalSlot">External slot</param>
        public void SetConsumerExternalSlot(int slot, int link, int externalSlot)
        {
            m_IntData[2 * m_NumConsumerLinks + slot * m_ConsumerSlotSetStride + link + 3] = externalSlot;
        }

        /// <summary>
        /// Gets the consumer external slot.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <param name="link">Link number</param>
        /// <returns>External slot</returns>
        public int GetConsumerExternalSlot(int slot, int link)
        {
            return m_IntData[2 * m_NumConsumerLinks + slot * m_ConsumerSlotSetStride + link + 3];
        }

        /// <summary>
        /// Sets the number of completed consumer links.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <param name="numCompletedLinks">Number of completed links</param>
        public void SetConsumerNumCompletedLinks(int slot, int numCompletedLinks)
        {
            m_IntData[2 * m_NumConsumerLinks + slot * m_ConsumerSlotSetStride + 1] = numCompletedLinks;
        }

        /// <summary>
        /// Gets the number of completed consumer links.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <returns>Number of completed links</returns>
        public int GetConsumerNumCompletedLinks(int slot)
        {
            return m_IntData[2 * m_NumConsumerLinks + slot * m_ConsumerSlotSetStride + 1];
        }

        /// <summary>
        /// Increment the number of completed links.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <returns>New value of num-completed-links</returns>
        public int IncConsumerNumCompletedLinks(int slot)
        {
            return ++m_IntData[2 * m_NumConsumerLinks + slot * m_ConsumerSlotSetStride + 1];
        }

        /// <summary>
        /// Sets the consumer link opposite to the given link.
        /// </summary>
        /// <param name="link">Link number</param>
        /// <param name="oppositeLink">Opposite link number</param>
        public void SetConsumerOppositeLink(int link, int oppositeLink)
        {
            m_IntData[link] = oppositeLink;
        }

        /// <summary>
        /// Gets the consumer link opposite to the given link.
        /// </summary>
        /// <param name="link">Link number</param>
        /// <returns>Opposite link number</returns>
        public int GetConsumerOppositeLink(int link)
        {
            return m_IntData[link];
        }

        /// <summary>
        /// Sets the consumer slots in use on the given link.
        /// </summary>
        /// <param name="link">Link number</param>
        /// <param name="slot">Index of slot set in use</param>
        public void SetConsumerSlotsInUse(int link, int slot)
        {
            m_IntData[m_NumConsumerLinks + link] = slot;
        }

        /// <summary>
        /// Gets the consumer slots in use on the given link.
        /// </summary>
        /// <param name="link">Link number</param>
        /// <returns>Index of slot set in use</returns>
        public int GetConsumerSlotsInUse(int link)
        {
            return m_IntData[m_NumConsumerLinks + link];
        }

        /// <summary>
        /// Sets the producer link opposite to the given link.
        /// </summary>
        /// <param name="link">Link number</param>
        /// <param name="oppositeLink">Opposite link number</param>
        public void SetProducerOppositeLink(int link, int oppositeLink)
        {
            m_IntData[m_ProducerOppositeLinksStart + link] = oppositeLink;
        }

        /// <summary>
        /// Gets the producer link opposite to the given link.
        /// </summary>
        /// <param name="link">Link number</param>
        /// <returns>Opposite link number</returns>
        public int GetProducerOppositeLink(int link)
        {
            return m_IntData[m_ProducerOppositeLinksStart + link];
        }

        /// <summary>
        /// Sets the producer slots in use on the given link.
        /// </summary>
        /// <param name="link">Link number</param>
        /// <param name="slot">Index of slot set in use</param>
        public void SetProducerSlotsInUse(int link, int slot)
        {
            m_IntData[m_ProducerOppositeLinksStart + m_NumProducerLinks + link] = slot;
        }

        /// <summary>
        /// Gets the producer slots in use on the given link.
        /// </summary>
        /// <param name="link">Link number</param>
        /// <returns>Index of slot set in use</returns>
        public int GetProducerSlotsInUse(int link)
        {
            return m_IntData[m_ProducerOppositeLinksStart + m_NumProducerLinks + link];
        }

        /// <summary>
        /// Sets the consumer tag for the specified slot.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <param name="tag">Tag object</param>
        public void SetConsumerTag(int slot, object tag)
        {
            m_ConsumerTags[slot] = tag;
        }

        /// <summary>
        /// Gets the consumer tag for the specified slot.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <returns>Tag object</returns>
        public object GetConsumerTag(int slot)
        {
            return m_ConsumerTags[slot];
        }

        /// <summary>
        /// Sets the producer tag for the specified slot.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <param name="tag">Tag object</param>
        public void SetProducerTag(int slot, object tag)
        {
            m_ProducerTags[slot] = tag;
        }

        /// <summary>
        /// Gets the producer tag for the specified slot.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <returns>Tag object</returns>
        public object GetProducerTag(int slot)
        {
            return m_ProducerTags[slot];
        }

        /// <summary>
        /// Sets the consumer free slot at the specified index.
        /// </summary>
        /// <param name="index">Free slot index</param>
        /// <param name="slot">Slot index</param>
        void SetConsumerFreeSlot(int index, int slot)
        {
            m_IntData[2 * m_NumConsumerLinks + m_NumConsumerSlots * m_ConsumerSlotSetStride + index] = slot;
        }

        /// <summary>
        /// Gets the consumer free slot at the specified index.
        /// </summary>
        /// <param name="index">Free slot index</param>
        /// <returns>Slot index</returns>
        int GetConsumerFreeSlot(int index)
        {
            return m_IntData[2 * m_NumConsumerLinks + m_NumConsumerSlots * m_ConsumerSlotSetStride + index];
        }

        /// <summary>
        /// Gets the next available consumer slot index.
        /// </summary>
        /// <returns>Free consumer slot index</returns>
        public int GetNextFreeConsumerSlot()
        {
            if (m_NumConsumerSlotsUsed < m_NumConsumerSlots)
            {
                // There are free slots available
                int nextFreeSlot = GetConsumerFreeSlot(m_ConsumerFreeSlotsReadIdx);
                m_ConsumerFreeSlotsReadIdx++;
                if (m_ConsumerFreeSlotsReadIdx == m_NumConsumerSlots)
                    m_ConsumerFreeSlotsReadIdx = 0;

                m_NumConsumerSlotsUsed++;

#if DEBUG
                if (nextFreeSlot >= m_NumConsumerSlots)
                    throw new ArgumentOutOfRangeException("Slot being returned is outside the allowed range.");
#endif

                return nextFreeSlot;
            }
            else
            {
                // No free slots available
                return NO_SLOT;
            }
        }

        /// <summary>
        /// Returns a used consumer slot index to the free slot pile.
        /// </summary>
        /// <param name="slot">Slot to put back</param>
        public void ReturnUsedConsumerSlot(int slot)
        {
#if DEBUG
            if (slot >= m_NumConsumerSlots)
                throw new ArgumentOutOfRangeException("Slot being returned is outside the allowed range.");
            if (slot == NO_SLOT)
                throw new ArgumentOutOfRangeException("Can only return an existing slot.");
#endif

            // Reset slot contents
            for (int link = 0; link < m_NumConsumerLinks; link++)
            {
                SetConsumerLinkStatus(slot, link, LinkStatus.Inactive);
                SetConsumerExternalSlot(slot, link, NO_SLOT);
            }
            SetConsumerOppositeSlot(slot, NO_SLOT);
            SetConsumerNumCompletedLinks(slot, 0);
            SetConsumerNextSlot(slot, NO_SLOT);
            SetConsumerTag(slot, null);

            m_NumConsumerSlotsUsed--;

            // Add to free list
            SetConsumerFreeSlot(m_ConsumerFreeSlotsWriteIdx, slot);
            m_ConsumerFreeSlotsWriteIdx++;
            if (m_ConsumerFreeSlotsWriteIdx == m_NumConsumerSlots)
                m_ConsumerFreeSlotsWriteIdx = 0;
        }

        /// <summary>
        /// Sets the producer free slot at the specified index.
        /// </summary>
        /// <param name="index">Free slot index</param>
        /// <param name="slot">Slot index</param>
        void SetProducerFreeSlot(int index, int slot)
        {
            m_IntData[m_ProducerOppositeLinksStart + 2 * m_NumProducerLinks + m_NumProducerSlots + index] = slot;
        }

        /// <summary>
        /// Gets the producer free slot at the specified index.
        /// </summary>
        /// <param name="index">Free slot index</param>
        /// <returns>Slot index</returns>
        int GetProducerFreeSlot(int index)
        {
            return m_IntData[m_ProducerOppositeLinksStart + 2 * m_NumProducerLinks + m_NumProducerSlots + index];
        }

        /// <summary>
        /// Gets the next available producer slot index.
        /// </summary>
        /// <returns>Free consumer slot index</returns>
        public int GetNextFreeProducerSlot()
        {
            // If all the slots are used then allocate space for 2 more slots
            if (m_NumProducerSlotsUsed < m_NumProducerSlots)
            {
                // No need to reallocate
            }
            else
            {                
                // Reallocate existing byte and int data
                byte[] newByteData = new byte[m_ByteData.Length + m_NumProducerLinks];
                for (int i = 0; i < m_ByteData.Length; i++)
                    newByteData[i] = m_ByteData[i];
                int[] newIntData = new int[m_IntData.Length + 6];
                for (int i = 0; i < m_IntData.Length - m_NumProducerSlots * 2; i++)
                    newIntData[i] = m_IntData[i];
                for (int i = m_IntData.Length - m_NumProducerSlots * 2; i < m_IntData.Length - m_NumProducerSlots; i++)
                    newIntData[i + 2] = m_IntData[i];
                for (int i = m_IntData.Length - m_NumProducerSlots; i < m_IntData.Length; i++)
                    newIntData[i + 4] = m_IntData[i];
                object[] newProducerTags = new object[m_NumProducerSlots + 2];
                for (int i = 0; i < m_NumProducerSlots; i++)
                    newProducerTags[i] = m_ProducerTags[i];
                m_ProducerTags = newProducerTags;

                m_ByteData = newByteData;
                m_IntData = newIntData;

                // Update dimensions and indices
                m_NumProducerSlots += 2;
                m_NumProducerSlotsUsed += 2;

                // Initialise new slots
                for (int slot = m_NumProducerSlots - 2; slot < m_NumProducerSlots; slot++)
                    ReturnUsedProducerSlot(slot);
            }

            int nextFreeSlot = GetProducerFreeSlot(m_ProducerFreeSlotsReadIdx);
            m_ProducerFreeSlotsReadIdx++;
            if (m_ProducerFreeSlotsReadIdx == m_NumProducerSlots)
                m_ProducerFreeSlotsReadIdx = 0;

            m_NumProducerSlotsUsed++;

            return nextFreeSlot;
        }

        /// <summary>
        /// Returns a used producer slot index to the free slot pile.
        /// </summary>
        /// <param name="slot">Slot to put back</param>
        public void ReturnUsedProducerSlot(int slot)
        {
#if DEBUG
            if (slot >= m_NumProducerSlots)
                throw new ArgumentOutOfRangeException("Slot being returned is outside the allowed range.");
            if (slot == NO_SLOT)
                throw new ArgumentOutOfRangeException("Can only return an existing slot.");
#endif

            // Reset slot contents
            for (int link = 0; link < m_NumActiveProducerLinks; link++)
                SetProducerLinkStatus(slot, link, LinkStatus.Inactive);
            SetProducerOppositeSlot(slot, NO_SLOT);
            SetProducerNextSlot(slot, NO_SLOT);
            SetProducerTag(slot, null);

            m_NumProducerSlotsUsed--;

            // Add to free list
            SetProducerFreeSlot(m_ProducerFreeSlotsWriteIdx, slot);
            m_ProducerFreeSlotsWriteIdx++;
            if (m_ProducerFreeSlotsWriteIdx == m_NumProducerSlots)
                m_ProducerFreeSlotsWriteIdx = 0;
        }

        /// <summary>
        /// Sets the producer next slot at the specified index.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <param name="nextSlot">Next slot index</param>
        public void SetProducerNextSlot(int slot, int nextSlot)
        {
            m_IntData[m_ProducerOppositeLinksStart + 2 * m_NumProducerLinks + 2 * m_NumProducerSlots + slot] = nextSlot;
        }

        /// <summary>
        /// Gets the producer next slot at the specified index.
        /// </summary>
        /// <param name="slot">Slot index</param>
        /// <returns>Next slot index</returns>
        public int GetProducerNextSlot(int slot)
        {
            return m_IntData[m_ProducerOppositeLinksStart + 2 * m_NumProducerLinks + 2 * m_NumProducerSlots + slot];
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="numConsumerLinks">Number of consumer links</param>
        /// <param name="numProducerLinks">Number of producer links</param>
        /// <param name="numConsumerSlots">Number of consumer slots</param>
        /// <param name="numInitialProducerSlots">Initial number of producer slots</param>
        /// <param name="singleActiveProducerLink">Whether only one producer link is active at a time in a given slot</param>
        public Slots(int numConsumerLinks, int numProducerLinks, int numConsumerSlots, int numInitialProducerSlots, bool singleActiveProducerLink)
        {
            // Store dimension parameters
            m_NumConsumerLinks = numConsumerLinks;
            m_NumProducerLinks = numProducerLinks;
            m_NumConsumerSlots = numConsumerSlots;
            m_NumProducerSlots = numInitialProducerSlots;
            m_SingleActiveProducerLink = singleActiveProducerLink;

            // Allocate tags arrays
            m_ConsumerTags = new object[m_NumConsumerSlots];
            m_ProducerTags = new object[m_NumProducerSlots];

            // Calculate starts & strides
            m_ConsumerSlotSetStride = 3 + m_NumConsumerLinks;
            m_ProducerOppositeLinksStart = 2 * m_NumConsumerLinks + m_NumConsumerSlots * m_ConsumerSlotSetStride + m_NumConsumerSlots;

            // Allocate the slot sets data array
            m_NumActiveProducerLinks = m_SingleActiveProducerLink ? 1 : m_NumProducerLinks;
            m_ByteData = new byte[(m_NumConsumerLinks * m_NumConsumerSlots + m_NumActiveProducerLinks * m_NumProducerSlots + 1) / 2];
            m_IntData = new int[m_ProducerOppositeLinksStart + 3 * m_NumProducerSlots + 2 * m_NumProducerLinks];

            // Set the slot sets data
            for (int slot = 0; slot < m_NumConsumerSlots; slot++)
            {
                for (int link = 0; link < m_NumConsumerLinks; link++)
                {
                    SetConsumerLinkStatus(slot, link, LinkStatus.Inactive);
                    SetConsumerExternalSlot(slot, link, NO_SLOT);
                }

                SetConsumerOppositeSlot(slot, NO_SLOT);
                SetConsumerNumCompletedLinks(slot, 0);
                SetConsumerNextSlot(slot, NO_SLOT);
            }
            for (int slot = 0; slot < m_NumProducerSlots; slot++)
            {
                for (int link = 0; link < m_NumActiveProducerLinks; link++)
                {
                    SetProducerLinkStatus(slot, link, LinkStatus.Inactive);
                }

                SetProducerOppositeSlot(slot, NO_SLOT);
                SetProducerNextSlot(slot, NO_SLOT);
            }
            for (int link = 0; link < m_NumConsumerLinks; link++)
            {
                SetConsumerOppositeLink(link, NO_LINK);
                SetConsumerSlotsInUse(link, NO_SLOT);
            }
            for (int link = 0; link < m_NumProducerLinks; link++)
            {
                SetProducerOppositeLink(link, NO_LINK);
                SetProducerSlotsInUse(link, NO_SLOT);
            }

            // Fill the consumer free-slots array
            for (int slot = 0; slot < m_NumConsumerSlots; slot++)
            {
                SetConsumerFreeSlot(slot, slot);
            }

            m_ConsumerFreeSlotsReadIdx = m_ConsumerFreeSlotsWriteIdx = 0;
            m_NumConsumerSlotsUsed = 0;

            // Fill the producer free-slots array
            for (int slot = 0; slot < m_NumProducerSlots; slot++)
            {
                SetProducerFreeSlot(slot, slot);
            }

            m_ProducerFreeSlotsReadIdx = m_ProducerFreeSlotsWriteIdx = 0;
            m_NumProducerSlotsUsed = 0;
        }

        /// <summary>
        /// Gets the number of consumer slots.
        /// </summary>
        public int NumConsumerSlots
        {
            get { return m_NumConsumerSlots; }
        }

        /// <summary>
        /// Gets the number of producer slots.
        /// </summary>
        public int NumProducerSlots
        {
            get { return m_NumProducerSlots; }
        }

        /// <summary>
        /// Gets the number of consumer links.
        /// </summary>
        public int NumConsumerLinks
        {
            get { return m_NumConsumerLinks; }
        }

        /// <summary>
        /// Gets the number of producer links.
        /// </summary>
        public int NumProducerLinks
        {
            get { return m_NumProducerLinks; }
        }
    }

}